package com.atguigu.gulimall.order.config;

import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.connection.CorrelationData;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter;
import org.springframework.amqp.support.converter.MessageConverter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import org.springframework.amqp.rabbit.connection.ConnectionFactory;

/**
 * RabbitMQ的配置类
 */
@Configuration
public class MyRabbitMQConfig {

    //@Autowired  没有直接注入,在加入 spring-boot-starter-actuator 依赖后，会有循环依赖的问题
    RabbitTemplate rabbitTemplate;

    //解决循环依赖问题：自己创建RabbitTemplate
    @Primary //将自己注入的 RabbitTemplate 设为主要的。
    @Bean
    public RabbitTemplate rabbitTemplate(ConnectionFactory connectionFactory){
        RabbitTemplate rabbitTemplate = new RabbitTemplate(connectionFactory);
        this.rabbitTemplate=rabbitTemplate;
        rabbitTemplate.setMessageConverter(messageConverter()); //设置：json序列化机制
        initRabbitTemplate();//设置：消息确认机制-可靠抵达
        return rabbitTemplate;
    }


    /**
     * 使用json替换默认的序列化规则。
     */
    @Bean
    public MessageConverter messageConverter() {
        return new Jackson2JsonMessageConverter();
    }


    /**
     * 消息确认机制-可靠抵达
     *  通过定制RabbitTemplate来实现：
     *
     * 1、服务端收到消息就回调 ConfirmCallback
     *      配置步骤：
     *      ①配置文件：spring.rabbitmq.publisher-confirms=true
     *      ②设置确认回调：RabbitTemplate.setConfirmCallback();
     *
     * 2、消息没有正确抵达队列进行回调 ReturnCallback
     *      配置步骤：
     *      ①配置文件：spring.rabbitmq.publisher-returns=true
     *      ②配置文件：spring.rabbitmq.template.mandatory=true（非必须）
     *      ③设置确认回调：RabbitTemplate.setReturnCallback();
     *
     * 3、消费端确认（保证每个消息被正确消费，此时才可以从Broker中删除这个消息）
     *      1)、默认是自动确认的，只要消息被接收到，客户端会自动确认，服务端就会移除这个消息。
     *          问题：
     *              监听某个消息队列，我们收到5条消息，客户端会自动回复给服务器ack。消息还没有处理完，客户端宕机了。
     *              此时服务器已经删除所有消息，发生了消息丢失。
     *          解决：
     *              手动确认，处理一个，确认一个。
     *              channel.basicAck(); 签收
     *              channel.basicNack(); 不签收
     *
     *     2）、手动确认模式下，只要我们没有通知服务器，没有ack。消息就一直是unacked状态。即使客户端宕机，消息也不会丢失。消息会重置为Ready状态，等待被取走。
     *          配置步骤：
     *          ①配置文件：spring.rabbitmq.listener.simple.acknowledge-mode=manual （默认是auto自动确认）
     *          ②在接受消息的方法形参列表中，声明Channer channer对象，代表信道。
     *          ③在方法体中，处理完消息后，调用channel.basicAck()等方法，手动ack。
     *          见：com.atguigu.gulimall.order.controller.MyRabbitMQController#recieveMessage
     */
//    @PostConstruct
    public void initRabbitTemplate() {

        //1、设置服务端收到消息就回调
        rabbitTemplate.setConfirmCallback(new RabbitTemplate.ConfirmCallback() {
            /**
             *
             * @param correlationData 当前消息的唯一关联数据（这个是消息的唯一id）
             * @param b 消息是否成功收到
             * @param s 失败的原因
             */
            @Override
            public void confirm(CorrelationData correlationData, boolean b, String s) {
                System.out.println("-----ConfirmCallback消息发送成功-->correlationData：" + correlationData + "--消息是否成功收到：" + b + "--失败的原因：" + s);
            }
        });

        //2、设置消息抵达队列确认回调（触发时机：消息没有投递给指定的队列，就触发这个回调）
        rabbitTemplate.setReturnCallback(new RabbitTemplate.ReturnCallback() {
            /**
             * @param message 投递失败的消息的详细信息
             * @param i 回复状态码
             * @param s 回复的文本内容
             * @param s1 当时这个消息发给哪个交换机
             * @param s2 当时这个消息的路由键
             */
            @Override
            public void returnedMessage(Message message, int i, String s, String s1, String s2) {
                System.out.println("-----ReturnCallback消息投递失败-->Message投递失败的消息本身：" + message + "回复状态码:" + i + "回复的文本内容:" + s + "当时这个消息发给哪个交换机:" + s1 + "当时这个消息的路由键:" + s2);
            }
        });


    }


}
